/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

/**
 * @file   list.c
 * @brief  Defines functions about list handling.
 * @note   Use list to sort elements.
 */
#include "list.h"

/**
 * @brief       To make a list
 *
 * @param[in]   name          #1: Name of the list
 *
 * @retval      Not NULL      #1: Pointer of the list that is created
 * @retval      NULL          #2: Failed to make the list.
 *
 * @note         errno is set when allocate memory failed.
 */
LIST_T *
gf_list_make(const char *name)
{
    LIST_T *list;

    list = (LIST_T *)calloc(1, sizeof(LIST_T));
    if (NULL == list) {
        return (NULL);
    }

    list->forw = list->back = list;

    /* Set name for the list. */
    list->name = strdup(name);
    if (NULL == list->name) {
        return (NULL);
    }

    /* List is created. */
    return (list);
} /* gf_list_make */


/**
 * @brief        Free up the list
 *
 * @param[in|out]    list    #1: The list to be freed
 * @param[in]        func    #2: Pointer of function that is to free each element in the list.
 *
 * @note       Pointer of function could be NULL
 */
void
gf_list_free(LIST_T *list, void (*func)(void *))
{
    LIST_T   *element;

    if (NULL == list) {
        return;
    }

    /* Go through the list */
    while (NULL != (element = gf_list_pop(list))) {
        if (NULL == func) {
            free(element);
        } else {
            (*func)(element);
        }
    }

    free(list->name);
    free(list);
} /* gf_list_free*/

void
gf_free_none(void *ent)
{
    return;
}

void
gf_free_proxy_string(void *ent)
{
    LIST_PROXY_T *pxy = (LIST_PROXY_T *)ent;

    free(pxy->subject);
    free(pxy);

    return;
}

/**
 * @brief       Insert the element after the specified element in the list.
 *
 * @param[in|out]   list             #1: List to which the element is inserted.
 * @param[in|out]   element          #2: Specified element after which the element is inserted.
 * @param[in|out]   insert_element   #3: Element to be inserted to the list.
 *
 * @retval      0               #1: The element is inserted to the list
 * @retval      -1              #2: One of input parameter is not valid.
 *
 * @note
 */
int
gf_list_insert(LIST_T *list, LIST_T *element, LIST_T *insert_element)
{
    if ((NULL == list) || (NULL == element) || (NULL == insert_element)) {
        return (-1);
    }

    element->back->forw = insert_element;
    insert_element->back = element->back;
    element->back = insert_element;
    insert_element->forw = element;

    list->numElements++;

    return (0);
} /* gf_list_insert */


/**
 * @brief        Insert element in a sorted list by increasing priority
 *
 * @param[in|out]    list        #1: List to which the element is inserted.
 * @param[in|out]    element     #2: Element to be inserted to the list
 * @param[in]        cmp         #3: Pointer of function to compare two elements.
 *
 * @retval       0           #1: The element is inserted to the list
 * @retval       -1          #2: One of input parameter is not valid.
 *
 * @note         cmp() is supposed to behave like compare function for qsort()
 */
int
gf_list_insert_sort(LIST_T *list, LIST_T *element,
                    int (*cmp)(const void *, const void *))
{
    LIST_T *cmp_element;
    int cmp_ret;

    if ((NULL == list) || (NULL == element)) {
        return (-1);
    }

    if (0 == LIST_NUM_ENTS(list)) { /* List has no element */
        gf_list_insert(list, list, element);
        return (0);
    }

    /* Go through element in the list */
    for (cmp_element = list->back; cmp_element != list; cmp_element = cmp_element->back) {

        cmp_ret = (*cmp)((void *)element, (void *)cmp_element);
        if (0 >= cmp_ret) { /* element has lower priority than cmp_element */

            gf_list_insert(list, cmp_element->forw, element);

            return (0);
        }
    }

    /* No element in the list has higher priority than element, insert the element to the
     * head of the list. */
    gf_list_insert(list, list->forw, element);

    return (0);
} /* gf_list_insert_sort */


/**
 * @brief      Remove an element from the list
 *
 * @param[in|out]  list        #1: List from which the element is removed.
 * @param[in|out]  element     #2: The element to be removed.
 *
 * @retval     Not NULL    #1: The removed element
 * @retval     NULL        #2: One of input parameters is not valid, or no element in the list.
 *
 * @note
 */
LIST_T *
gf_list_remove(LIST_T *list, LIST_T *element)
{
    if (NULL == list || NULL == element) {
        return (NULL);
    }

    if (0 == list->numElements) { /* No element in the list. */
        return (NULL);
    }

    element->back->forw = element->forw;
    element->forw->back = element->back;
    list->numElements--;

    return (element);
} /* gf_list_remove */


/**
 * @brief       Pop from front of the list.
 *
 * @param[in|out]   list      #1: The list.
 *
 * @retval      Not NULL      #1: Head element of the list.
 * @retval      NULL          #2: No element in the list.
 *
 * @note        The element is removed from the list, caller should free the element itself.
 */
LIST_T *
gf_list_pop(LIST_T *list)
{
    LIST_T *element;

    if (NULL == list) {
        return (NULL);
    }

    if (list->forw == list) {
        return (NULL);
    }

    element = gf_list_remove(list, list->forw);

    return (element);
} /* gf_list_pop */

LIST_T*
gf_list_duplicate(LIST_T* list, int sizeOfEntry)
{
    LIST_T *newList;
    LIST_T *listEntry;
    LIST_T *newListEntry;
    LIST_ITERATOR_T iter;

    newList = gf_list_make(list->name);
    if (NULL == newList) {
        return NULL;
    }

    memset(&iter, 0, sizeof(LIST_ITERATOR_T));
    iter.name = (char *)"";

    gf_listiterator_link(&iter, list);

    for (listEntry = gf_listiterator_curentry(&iter);
         !gf_listiterator_isend(&iter);
         gf_listiterator_getnext(&iter, &listEntry)) {

        newListEntry = (LIST_T *)calloc(1, sizeOfEntry);
        if (NULL == newListEntry) {
            return NULL;
        }

        memcpy(newListEntry, listEntry, sizeOfEntry);

        gf_list_insert(newList, newList->forw, newListEntry);
    }

    return newList;
}

LIST_T *
gf_list_search(LIST_T *list, void *subject, int (*equal)(void *, void *))
{
    LIST_ITERATOR_T iter;
    LIST_T *entry;

    memset(&iter, 0, sizeof(LIST_ITERATOR_T));
    iter.name = (char *)"";

    gf_listiterator_link(&iter, list);

    for (entry = gf_listiterator_curentry(&iter); !gf_listiterator_isend(&iter);
         gf_listiterator_getnext(&iter, &entry)) {

        if ((*equal)((void *)entry, subject) != 0) {
            return entry;
        }
    }

    return NULL;
}

int
gf_listiterator_link(LIST_ITERATOR_T *iter, LIST_T *list)
{
    iter->list = list;
    iter->curentry = list->forw;

    return 0;
}

LIST_T *
gf_listiterator_curentry(LIST_ITERATOR_T *iter)
{
    if (iter->curentry == (LIST_T *)iter->list)
        return NULL;

    return iter->curentry;
}

void
gf_listiterator_getnext(LIST_ITERATOR_T *iter, LIST_T **next)
{
    iter->curentry = iter->curentry->forw;
    *next = gf_listiterator_curentry(iter);

    return;
}

void
gf_listiterator_getprev(LIST_ITERATOR_T *iter, LIST_T **prev)
{
    iter->curentry = iter->curentry->back;
    *prev = gf_listiterator_curentry(iter);

    return;
}

int
gf_listiterator_isend(LIST_ITERATOR_T *iter)
{
    return (iter->curentry == (LIST_T *)iter->list);
}
